#! /usr/bin/env python

# -*- coding: UTF-8 -*-
# ==============================================================================
# Filename : TabWidget.py
# Purpose  :
# Author   : Yohann Li (lyh001686@sina.com)
# Date     : 2020-07-01 17:04
# ==============================================================================
import functools
import os
import sys

from PyQt5.QtCore import Qt, pyqtSignal, QObject
from PyQt5.QtGui import QKeySequence, QTextCursor
from PyQt5.QtWidgets import QTabWidget, QShortcut, QStatusBar, QMessageBox, QApplication
import PyQt5.sip as sip

# from Widgets.SerialPage import SerialFrame
from Widgets.MiniMap import MiniMap
# from Widgets.QSerialPage import SerialFrame
from Widgets.SerialPage import SerialFrame, ReadSerial
from Widgets.ReaderPage import Frame as ReaderFrame
from Widgets.TabBar import TabBar
from Widgets.TextEdit import TextEdit as SerialEdit
from Widgets.ReaderEdit import TextEdit as ReaderEdit
from conf.configuration import logger


class TabWidget(QTabWidget):
    open_serial_signal = pyqtSignal()

    def __init__(self, *args, **kwargs):
        super(TabWidget, self).__init__(*args, **kwargs)
        self.status_bar: QStatusBar = self.parent().statusBar()
        self.setTabBar(TabBar())
        self.add_tab()
        self.set_shortcut()
        self.set_signal()
        self.setUsesScrollButtons(True)
        self.setTabsClosable(True)
        self.setMovable(True)
        self.setLayoutDirection(Qt.LeftToRight)
        # self.tabCloseRequested.connect(self.rm_tab)

    def set_shortcut(self):
        QShortcut(QKeySequence.AddTab, self).activated.connect(self.add_tab)
        QShortcut(QKeySequence.Close, self).activated.connect(self.rm_tab)

        for _num in range(1, 10):
            QShortcut(QKeySequence(f'Ctrl+{_num}'), self).activated.connect(
                functools.partial(self.setCurrentIndex, _num-1))

        QShortcut(QKeySequence.Find, self).activated.connect(self._search_bar_showed)  # Cmd+F
        QShortcut(QKeySequence.Cancel, self).activated.connect(self.hide_search_bar)  # ESC

    def hide_search_bar(self):
        logger.debug(f"Hiding search bar")
        self.currentWidget().search_bar.hide()

    def set_signal(self):
        self.open_serial_signal.connect(self.add_serial_tab)
        self.tabCloseRequested[int].connect(self.send_close_tab_signal)

    def _search_bar_showed(self):
        """搜索栏出现的时候focus到搜索框"""
        self.currentWidget().search_bar_show_event()

    def send_close_tab_signal(self, index=None):
        index = index or self.currentIndex()
        logger.debug(f"Removing widget {index}")
        widget_ = self.widget(index)
        if self.maybe_save(index):
            widget_.close() if isinstance(self.widget(index), SerialFrame) else ''
            self.removeTab(index)
            for o_ in widget_.findChildren(QObject):
                print(f"deleting {o_} type: {isinstance(o_, ReadSerial)}")
                if not sip.isdeleted(o_) and not isinstance(o_, ReadSerial):
                    sip.delete(o_)
            else:
                sip.delete(widget_) if not sip.isdeleted(widget_) else ''
        else:
            pass

    def maybe_save(self, index: int):
        index = index or self.currentIndex()
        # logger.debug(f"modified: {self.document().isModified()}")
        if not isinstance(self.widget(index), SerialFrame) and self.widget(index).t.document().isModified():
            ret = QMessageBox.warning(self, "title won't be shown",
                                      f"{self.widget(index).t.file_name or 'File'} has been modified.\nDo you want to save your changes?",
                                      QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel)

            if ret == QMessageBox.Save:
                return self.widget(index).t.save()

            if ret == QMessageBox.Cancel:
                return False

        return True

    def rm_tab(self, index=None):
        """移除指定tab或当前tab"""
        index = index or self.currentIndex()
        if index == '-1':
            return
        logger.debug(f"Removing widget {index}")
        self.tabCloseRequested.emit(index)

    def add_tab(self):
        """添加新tab并设置标签名"""
        tab_count = self.count()
        new_frame_name = ''
        new_frame = ReaderFrame(new_frame_name)
        self.addTab(new_frame, f'Page {tab_count+1}')
        self.setCurrentWidget(new_frame)
        new_frame.findChild(ReaderEdit).open_pySignal.connect(self.set_text)
        new_frame.findChild(ReaderEdit).title_pySignal.connect(self.set_tab_title)

    def add_serial_tab(self):
        tab_count = self.count()
        new_frame_name = f'Page {tab_count + 1}'
        new_frame = SerialFrame(new_frame_name)
        self.addTab(new_frame, new_frame_name)
        self.setCurrentWidget(new_frame)
        new_frame.set_status_signal.connect(self.parent().set_status_bar_msg)  #
        new_frame.findChild(SerialEdit).title_pySignal.connect(self.set_tab_title)

    def set_text(self, path: str):
        """读取文件并填充到ReaderEdit"""
        self.set_tab_title(path)
        text = self.read_file_by_directly(path)
        text_edit = self.currentWidget().findChild(ReaderEdit)
        text_edit.clear()
        text_edit.setPlainText(text)
        text_edit.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
        minimap = self.currentWidget().findChild(MiniMap)
        minimap.clear()
        minimap.setPlainText(text)
        minimap.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)

    def set_tab_title(self, path: str):
        file_name = os.path.basename(path)
        self.setTabText(self.currentIndex(), file_name)
        if isinstance(self.currentWidget(), SerialFrame):
            self.currentWidget().findChild(SerialEdit).setObjectName(file_name)
        else:
            self.currentWidget().findChild(ReaderEdit).setObjectName(file_name)
        self.currentWidget().findChild(MiniMap).setObjectName(file_name)

    @functools.lru_cache()
    def read_file_by_directly(self, path: str):
        """直接使用python读取文件"""
        if os.path.isdir(path):  # 如果拖目录进去，则终止
            logger.warning(f"{path} is directory, not a file!")

            return
        with open(path, 'rb') as f:
            logger.debug(f'Reading file {path}')
            text = f.read().decode('utf8', errors='ignore')
            logger.debug(f"Finished to read file of size {os.path.getsize(path)}")
            return text

    def clear_edit_content(self):
        """清空 editor和minimap的内容"""
        if isinstance(self.currentWidget(), SerialFrame):
            self.currentWidget().findChild(SerialEdit).clear()
        else:
            self.currentWidget().findChild(ReaderEdit).clear()
        self.currentWidget().findChild(MiniMap).clear()

    def closeEvent(self, a0) -> None:
        if self.maybe_save(self.currentIndex()):
            a0.accept()
        else:
            a0.ignore()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    UI = TabWidget()
    UI.show()
    sys.exit(app.exec_())